﻿using Dapper;

using System;
using System.Collections.Generic;
using System.Data;
using System.Threading.Tasks;

namespace Tszy.Unlimited.Data.AdoNet.Sql.Providers.Dapper
{
    /// <summary>
    ///
    /// </summary>
    public class DapperDataProvider : IDataProvider
    {
        private readonly Func<IDbConnection> _action;

        /// <summary>
        ///
        /// </summary>
        /// <param name="action"></param>
        /// <param name="databaseType"></param>
        public DapperDataProvider(Func<IDbConnection> action, DatabaseType databaseType)
        {
            _action = action;
            DatabaseType = databaseType;
        }

        /// <summary>
        ///
        /// </summary>
        public DatabaseType DatabaseType { get; }

        /// <summary>
        ///
        /// </summary>
        /// <returns></returns>
        public IDbConnection GetConnection()
        {
            return _action?.Invoke();
        }

        /// <summary>
        ///
        /// </summary>
        /// <param name="sql"></param>
        /// <param name="parameters"></param>
        /// <param name="transaction"></param>
        /// <returns></returns>
        public int Execute(string sql, object parameters, IDbTransaction transaction)
        {
            if (transaction?.Connection != null)
            {
                return transaction.Connection.Execute(sql, parameters, transaction);
            }

            using var connection = GetConnection();
            return connection.Execute(sql, parameters, transaction);
        }

        /// <summary>
        ///
        /// </summary>
        /// <param name="sql"></param>
        /// <param name="parameters"></param>
        /// <param name="transaction"></param>
        /// <returns></returns>
        public async Task<int> ExecuteAsync(string sql, object parameters, IDbTransaction transaction)
        {
            if (transaction?.Connection != null)
            {
                return await transaction.Connection.ExecuteAsync(sql, parameters, transaction);
            }

            using var connection = GetConnection();
            return await connection.ExecuteAsync(sql, parameters, transaction);
        }

        /// <summary>
        ///
        /// </summary>
        /// <param name="sql"></param>
        /// <param name="parameters"></param>
        /// <param name="transaction"></param>
        /// <returns></returns>
        public object Field(string sql, object parameters, IDbTransaction transaction)
        {
            if (transaction?.Connection != null)
            {
                return transaction.Connection.ExecuteScalar(sql: sql, param: parameters, transaction: transaction);
            }

            using var connection = GetConnection();
            return connection.ExecuteScalar(sql: sql, param: parameters, transaction: transaction);
        }

        /// <summary>
        ///
        /// </summary>
        /// <param name="sql"></param>
        /// <param name="parameters"></param>
        /// <param name="transaction"></param>
        /// <returns></returns>
        public async Task<object> FieldAsync(string sql, object parameters, IDbTransaction transaction)
        {
            if (transaction?.Connection != null)
            {
                return await transaction.Connection.ExecuteScalarAsync(sql: sql, param: parameters, transaction: transaction);
            }

            using var connection = GetConnection();
            return await connection.ExecuteScalarAsync(sql: sql, param: parameters, transaction: transaction);
        }

        /// <summary>
        ///
        /// </summary>
        /// <typeparam name="TSource"></typeparam>
        /// <param name="sql"></param>
        /// <param name="parameters"></param>
        /// <param name="transaction"></param>
        /// <returns></returns>
        public TSource Field<TSource>(string sql, object parameters, IDbTransaction transaction)
        {
            if (transaction?.Connection != null)
            {
                return transaction.Connection.ExecuteScalar<TSource>(sql, parameters, transaction);
            }

            using var connection = GetConnection();
            return connection.ExecuteScalar<TSource>(sql, parameters, transaction);
        }

        /// <summary>
        ///
        /// </summary>
        /// <typeparam name="TSource"></typeparam>
        /// <param name="sql"></param>
        /// <param name="parameters"></param>
        /// <param name="transaction"></param>
        /// <returns></returns>
        public async Task<TSource> FieldAsync<TSource>(string sql, object parameters, IDbTransaction transaction)
        {
            if (transaction?.Connection != null)
            {
                return await transaction.Connection.ExecuteScalarAsync<TSource>(sql, parameters, transaction);
            }

            using var connection = GetConnection();
            return await connection.ExecuteScalarAsync<TSource>(sql, parameters, transaction);
        }

        /// <summary>
        ///
        /// </summary>
        /// <typeparam name="TSource"></typeparam>
        /// <param name="sql"></param>
        /// <param name="parameters"></param>
        /// <param name="transaction"></param>
        /// <returns></returns>
        public IEnumerable<TSource> Query<TSource>(string sql, object parameters, IDbTransaction transaction)
        {
            if (transaction?.Connection != null)
            {
                return transaction.Connection.Query<TSource>(sql, parameters, transaction);
            }

            using var connection = GetConnection();
            return connection.Query<TSource>(sql, parameters, transaction);
        }

        /// <summary>
        ///
        /// </summary>
        /// <typeparam name="TSource"></typeparam>
        /// <param name="sql"></param>
        /// <param name="parameters"></param>
        /// <param name="transaction"></param>
        /// <returns></returns>
        public async Task<IEnumerable<TSource>> QueryAsync<TSource>(string sql, object parameters, IDbTransaction transaction)
        {
            if (transaction?.Connection != null)
            {
                return await transaction.Connection.QueryAsync<TSource>(sql, parameters, transaction);
            }

            using var connection = GetConnection();
            return await connection.QueryAsync<TSource>(sql, parameters, transaction);
        }

        /// <summary>
        ///
        /// </summary>
        /// <param name="type"></param>
        /// <param name="sql"></param>
        /// <param name="parameters"></param>
        /// <param name="transaction"></param>
        /// <returns></returns>
        public IEnumerable<object> Query(Type type, string sql, object parameters, IDbTransaction transaction)
        {
            if (transaction?.Connection != null)
            {
                return transaction.Connection.Query<object>(sql, parameters, transaction);
            }

            using var connection = GetConnection();

            return connection.Query<object>(sql, parameters, transaction);
        }

        /// <summary>
        ///
        /// </summary>
        /// <param name="type"></param>
        /// <param name="sql"></param>
        /// <param name="parameters"></param>
        /// <param name="transaction"></param>
        /// <returns></returns>
        public async Task<IEnumerable<object>> QueryAsync(Type type, string sql, object parameters, IDbTransaction transaction)
        {
            if (transaction?.Connection != null)
            {
                return await transaction.Connection.QueryAsync<object>(sql, parameters, transaction);
            }

            using var connection = GetConnection();

            return await connection.QueryAsync<object>(sql, parameters, transaction);
        }
    }
}